﻿/*
 *  Source code courtesy of the desktopWeb.CodePlex.com community project. See MS-PL license on Codeplex.com. 
 *  This is beta code not intended for a production application.
 */


//TODO: Clean references
using System;
using System.Activities.Presentation.View;
using System.Windows;
using System.Reflection;
using System.Windows.Controls;
using System.Windows.Media;
using System.Collections.Generic;
using System.Activities.Presentation.Model;
using System.Windows.Media.Imaging;
using System.Windows.Documents;
using csom = MyData.SharePoint;
using MyData.ExtensionMethods;
using Microsoft.SharePoint.Client;
using System.Xml.Linq;
using System.Linq;
using System.Activities;
using System.Activities.XamlIntegration;
using io = System.IO;
using forms = System.Windows.Forms;
using System.Net;
using System.ComponentModel.Composition;
using System.Collections.ObjectModel;
using MyData.ExpressionBuilder.Interfaces;
using MyData.ExpressionBuilder.Model;
using System.Windows.Threading;
using System.Windows.Forms;
using System.Threading;
using MyData.ExpressionBuilder;
using MyData.ExpressionBuilder.BaseExtensions;
using MyData.SharePoint;
using Microsoft.VisualBasic.Activities;
using System.Data.SqlClient;

namespace MyData.Workflow.Designer
{
  [Export]
  public class MyExpressionEditor : IExpressionEditorInstance
  {
    //MEF
    [ImportMany(AllowRecomposition = true)]
    public ObservableCollection<Lazy<IExpressionExtension, IExpressionBuilderMetadata>> extensions { get; set; }

    //Base ExpressionEditor
    //Create expression editor control to give to workflow designer. This is the controls with the context MenuItem
    ExpressionEditorUserControl control = new ExpressionEditorUserControl();
    
    //Context MenuItem
    private System.Windows.Controls.MenuItem expressionMenu = null;
    
    //Target ModelItem
    private string _expressionPath = string.Empty;
    
    
    public string ExpressionPath
    {
      get
      {
        return _expressionPath;
      }

      set
      {
        _expressionPath = value;
      }
    }
    public SqlConnectionStringBuilder ConnectionStringBuilder { get; set; }
    public ModelItem ExpressionModelItem { get; set; }
    public ModelItem OwnerActivity { get; set; }

    //ModelItem ExpressionMappings for MEF extensions
    Dictionary<string, ExpressionMapping> expressionMappings;

    //MEF ExpressionBuilder extension
    IExpressionBuilder pathBuilder;
    //Next: Rethink ExtensionContextName
    string ExtensionContextName = string.Empty;
    
    #region Setup HostControl including converters and builders
    //Host expression control
    public System.Windows.Controls.Control HostControl
    {
      get
      {
        control.Loaded += new RoutedEventHandler(control_Loaded);
        control.expressionText.PreviewKeyDown += new System.Windows.Input.KeyEventHandler(expressionText_PreviewKeyDown);                
        control.expressionText.IsVisibleChanged += new DependencyPropertyChangedEventHandler(expressionText_IsVisibleChanged);
        control.expressionText.LostFocus += new RoutedEventHandler(expressionText_LostFocus);
        control.UnloadHost += new EventHandler(control_UnloadHost);

        return control;
      }
    }

    //Load ExpressionMenu
    //Currently contains converters and builders
    void control_Loaded(object sender, RoutedEventArgs e)
    {
      if (expressionMenu == null)
      {
        expressionMenu = (System.Windows.Controls.MenuItem)control.expressionMenu.FindName("expressionMenu");
        expressionMenu.Items.Clear();
        expressionMenu.Visibility = Visibility.Hidden;

        //Always load?
        expressionMappings = ExpressionMappings.Fill(MyData.Workflow.Designer.Properties.Settings.Default.ExpressionEditor);

        //Resolve MEF builder
        pathBuilder = this.ResolveBuilder(this.ExpressionPath);

        if (pathBuilder != null)
        {
          pathBuilder.ItemsProvider = Extension<IExpressionItemsProvider>
            (this.ExpressionPath, ExpressionMappings.ExtensionAttributes.itemsProviderType.ToString());

          pathBuilder.Converter = Extension<IExpressionTypeConverter>
            (this.ExpressionPath, ExpressionMappings.ExtensionAttributes.converterType.ToString());

          pathBuilder.OnUpdated = Builder_OnUpdated;

          expressionMenu.Visibility = Visibility.Visible;
        }

        //Note: This is temporary. There is a bug that is causing the HostControl to load twice.
        if (this.ExpressionPath != null)
        {
          expressionMenu.Click += new RoutedEventHandler(ExpressionMenu_Click);
        }  
      }
    }

    #region Resolve ModelItem Extensions
    private TExtensionType Extension<TExtensionType>(string path, string extensionAttribute)
    {
      TExtensionType extension = default(TExtensionType);

      List<Lazy<IExpressionExtension, IExpressionBuilderMetadata>> targetExtensions = null;

      if (expressionMappings.ContainsKey(path))
      {
        if (expressionMappings[path].ExtensionElement.Attribute(extensionAttribute) != null)
        {
          string typeName = expressionMappings[path].ExtensionElement.Attribute(extensionAttribute).Value;

          //extension.GetType().ToString() style not used so a target extension is not coupled to the type.
          //The only coupling is the MetaData attribute
          //MetaData style
          targetExtensions =
            (extensions.Where(v => v.Metadata.Name.Equals(typeName)).Select(v => v)).ToList<Lazy<IExpressionExtension, IExpressionBuilderMetadata>>();

          if (targetExtensions.Count > 0)
          {
            extension = (TExtensionType)targetExtensions[0].Value;

            //Next: Rethink ExtensionContextName
            ExtensionContextName = targetExtensions[0].Metadata.Name;
          }
        }
      }

      return extension;
    }
   
    private IExpressionBuilder ResolveBuilder(string path)
    {
      IExpressionBuilder extension = Extension<IExpressionBuilder>
        (path, ExpressionMappings.ExtensionAttributes.builderType.ToString());

      //Next: Rethink ExtensionContextName
      if (ExtensionContextName == "MenuItemsBuilder")
      {
        extension.ItemsControl = this.expressionMenu;
      }

      return extension;

    }  
    #endregion

    void ExpressionMenu_Click(object sender, RoutedEventArgs e)
    {
     
      if (pathBuilder != null)
      {
        //NEXT: Use ModelItemExpression class
        pathBuilder.Run(new List<MyData.ExpressionBuilder.Model.ListItem> 
        { new MyData.ExpressionBuilder.Model.ListItem { Name = "ModelItem", Value = this.ExpressionModelItem }, 
          new MyData.ExpressionBuilder.Model.ListItem { Name = "Path", Value = this.ExpressionPath },
          new MyData.ExpressionBuilder.Model.ListItem { Name = "OwnerActivity", Value = this.OwnerActivity },
          new MyData.ExpressionBuilder.Model.ListItem { Name = "VariableNames", Value = this.VariableNames },
          new MyData.ExpressionBuilder.Model.ListItem { Name = "ExpressionType", Value = this.ExpressionType },
          new MyData.ExpressionBuilder.Model.ListItem { Name = "ConnectionStringBuilder", Value = this.ConnectionStringBuilder },
        });
      }   
    }

    private List<string> VariableNames
    {
      get
      {
        List<string> values = new List<string>();
        foreach (ModelItem mi in this.Variables)
        {
          values.Add(mi.Properties["Name"].ComputedValue.ToString());
        }
        return values;
      }
    }

    private void Builder_OnUpdated(object message)
    {
      if (message != null)
        control.expressionText.DocumentText(message.ToString());

      //Commit Expression Text
      this._commitedText = control.expressionText.DocumentText();
      LostAggregateFocus(null, null);
    }

    #endregion

    #region Expression RichTextBox
    void expressionText_PreviewKeyDown(object sender, System.Windows.Input.KeyEventArgs e)
    {
      if (e.Key == System.Windows.Input.Key.Enter)
      {
        LostAggregateFocus(sender, e);
      }
    }

    //Is visible so set focus to expression RichTextBox
    void expressionText_IsVisibleChanged(object sender, DependencyPropertyChangedEventArgs e)
    {
      System.Windows.Controls.RichTextBox rt;

      if ((bool)e.NewValue == true)
      {
        rt = sender as System.Windows.Controls.RichTextBox;
        if (this.Text != null)
          rt.DocumentText(this.Text);

        control.expressionText.Focus();
      }
    }
    #endregion

    #region Commit expression
    void control_UnloadHost(object sender, EventArgs e)
    {
      LostAggregateFocus(sender, e);
    }

    void expressionText_LostFocus(object sender, RoutedEventArgs e)
    {
      System.Windows.Controls.RichTextBox rt = sender as System.Windows.Controls.RichTextBox;
   
      this._commitedText = rt.DocumentText();
      LostAggregateFocus(sender, e);
    }

    private string _commitedText = string.Empty;
    public string CommitedText
    {
      get{
        return _commitedText;
      }
      set{
        _commitedText = value;
      }
    }

    public string GetCommittedText()
    {
      return _commitedText;
    }
    #endregion

    #region Implemented Members
    public bool AcceptsReturn { get; set; }
    public bool AcceptsTab { get; set; }
    public bool HasAggregateFocus
    {
      get
      {
        return true;
      }
    }
    public System.Windows.Controls.ScrollBarVisibility HorizontalScrollBarVisibility { get; set; }
    public int MaxLines { get; set; }
    public int MinLines { get; set; }
    public string Text
    {
      get;
      set;
    }
    public System.Windows.Controls.ScrollBarVisibility VerticalScrollBarVisibility { get; set; }
    public event EventHandler Closing;
    public event EventHandler GotAggregateFocus;
    public event EventHandler LostAggregateFocus;
    public event EventHandler TextChanged;
    public bool CanCompleteWord()
    {
      return true;
    }
    public bool CanCopy()
    {
      return true;
    }
    public bool CanCut()
    {
      return true;
    }
    public bool CanDecreaseFilterLevel()
    {
      return true;
    }
    public bool CanGlobalIntellisense()
    {
      return true;
    }
    public bool CanIncreaseFilterLevel()
    {
      return true;
    }
    public bool CanParameterInfo()
    {
      return true;
    }
    public bool CanPaste()
    {
      return true;
    }
    public bool CanQuickInfo()
    {
      return true;
    }
    public bool CanRedo()
    {
      return true;
    }
    public bool CanUndo()
    {
      return true;
    }
    public void ClearSelection()
    {

    }
    public void Close()
    {

    }
    public bool CompleteWord()
    {
      return true;
    }
    public bool Copy()
    {
      return true;
    }
    public bool Cut()
    {
      return true;
    }
    public bool DecreaseFilterLevel()
    {
      return true;
    }
    public void Focus()
    {

    }
    public bool GlobalIntellisense()
    {
      return true;
    }
    public bool IncreaseFilterLevel()
    {
      return true;
    }
    public bool ParameterInfo()
    {
      return true;
    }
    public bool Paste()
    {
      return true;
    }
    public bool QuickInfo()
    {
      return true;
    }
    public bool Redo()
    {
      return true;
    }
    public bool Undo()
    {
      return true;
    }
    #endregion

    public List<ModelItem> Variables
    {
      get;
      set;
    }
    
    public Type ExpressionType
    {
      get;
      set;
    }
  }
}